/** Cluster application **/

/* MPPA headers */
#include <mppaipc.h>
#include <mppa/osconfig.h>

#include <stdio.h>
#include <pthread.h>

#define MPPA_TRACEPOINT_DEFINE
#include "test-trace.h"
#define MPPA_CREATE_TRACEPOINT
#include "mppa_trace.h"


/* USER headers */
#include "Functions.h"

#define CONFIGURE_MAXIMUM_POSIX_THREADS 4
#define NUM_THREADS 3

int param[NUM_THREADS];

// modified function from http://codearcana.com/posts/2013/05/18/achieving-maximum-memory-bandwidth.html
void * write_memory_loop(void *arg) {
  size_t size = ((size_t *)arg)[0];
  size_t* carray = (size_t*) array;
  size_t i;
  for (i = 0; i < size / sizeof(size_t); i++) {
    carray[i] = 1;
  }
}

void *task(void *arg){
     int results;
     int i;
     for (i=0; i<=10000; i=i+5) {
        results = usr_main(i);
     }
     //int id = ((int *)arg)[0];
     //printf("Hello from %d\n", id);
     /*
       * retval Where to store the exit status.
     */
     // void pthread_exit(void *retval);
     // By default this function purge the core cache of the calling thread
     pthread_exit(NULL);
     return NULL;
}

int main(int argc, char* argv[]) {
	// get arguments
	int rank = atoi(argv[0]);
    const char *global_sync = argv[1]; 
    const char *results_portal = argv[2];	
    
	// set mask (e.g. 1000..0) 
	long long mask = (long long)1 << rank;
	
	// open Wx connectors	
	int global_sync_fd = mppa_open(global_sync, O_WRONLY);
    int results_portal_fd = mppa_open(results_portal, O_WRONLY);

	mppa_trace_init();
	
    // contributes to unblock the I/O, writes on sync with the mask value
	mppa_write(global_sync_fd, &mask, sizeof(mask));

    pthread_t t[NUM_THREADS];
    int i, res;
    void *ret;
    int ret_code;
    printf("Multi threaded test, rank = %d\n", rank);
   
    for (i = 0; i < NUM_THREADS; i++) {
        param[i] = i;
        if (i == 0) res = pthread_create(&t[i], NULL, task, NULL);                   
        else res = pthread_create(&t[i], NULL, write_memory_loop, NULL);             
        if (res != 0) {
           printf("pthread_create failed i = %d, res = %d\n", i, res);
           abort();
        }
        /*
          * pthread Pointer where to store the thread ID.
          * attr If not NULL, determine the new thread attributes.
          * start Pointer to the routine where thread execution starts.
          * arg Argument passed to the start routine.
          * return 0 on success, else non-zero error number.
        */
        // int pthread_create(pthread_t *pthread, const pthread_attr_t *attr, void *(*start)(void *), void *arg);
        // By default this function purge the core cache of the calling thread. The cache of the newly allocated core is invalidated before thread launch.
        // ex: pthread_create(&t[i], NULL, task, &param[i])
    }
    for (i = 0; i < NUM_THREADS; i++) {
        /*
          * pthread_id ID of the target thread to wait for.
          * retval If not NULL, the exit status is stored into **retval.
          * return 0 on success, else non-zero error number.
        */
        // int pthread_join(pthread_t pthread_id, void **retval);
        // By default this function invalidate the core cache of the calling thread.
        pthread_join(t[i], NULL);
    }
    printf("END OF TEST\n");

    

   
    
// END USER CODE

	// writes on the portal
	mppa_pwrite(results_portal_fd, &rank, sizeof(long int), sizeof(long int)*rank);
}












